package org.example.dao;

import com.mxgraph.layout.mxIGraphLayout;
import com.mxgraph.layout.mxOrganicLayout;
import org.jgrapht.GraphPath;
import org.jgrapht.alg.shortestpath.DijkstraShortestPath;
import com.mxgraph.util.mxCellRenderer;
import com.mxgraph.util.mxConstants;
import org.jgrapht.Graph;
import org.jgrapht.ext.JGraphXAdapter;
import org.jgrapht.graph.DefaultWeightedEdge;
import org.example.utils.OsUtils;
import java.util.*;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;

import java.io.FileWriter;
import java.io.IOException;
import java.util.List;

import org.jgrapht.graph.DefaultDirectedWeightedGraph;

public class WordGraph {
    private Graph<String, DefaultWeightedEdge> graph;
    private Map<DefaultWeightedEdge, String> edgeColors; // Map to store edge colors
    private String defaultColor;
    private OsUtils osUtils = new OsUtils();
    ;

    // 无参构造器
    public WordGraph() {
        graph = new DefaultDirectedWeightedGraph<>(DefaultWeightedEdge.class);
        edgeColors = new HashMap<>();
        defaultColor = "#000000";
    }

    // 构造函数，从整理好的字符串构建有向图
    public WordGraph(String text) {
        this();
        buildGraphFromText(text);
    }

    private void buildGraphFromText(String text) {
        String[] words = text.split("\\s+");
        Map<String, Integer> edgeWeights = new HashMap<>(); // 初始化边权值映射

        // 遍历每个单词对，更新边的权值
        for (int i = 0; i < words.length - 1; i++) {
            String source = words[i].toLowerCase();
            String target = words[i + 1].toLowerCase();
            String edgeKey = source + "," + target;

            // 添加或更新边的权值
            if (!graph.containsVertex(source)) {
                graph.addVertex(source);
            }
            if (!graph.containsVertex(target)) {
                graph.addVertex(target);
            }
            if (graph.containsEdge(source, target)) {
                int weight = edgeWeights.getOrDefault(edgeKey, 0) + 1;
                edgeWeights.put(edgeKey, weight);
                DefaultWeightedEdge edge = graph.getEdge(source, target);
                graph.setEdgeWeight(edge, weight);
                edgeColors.put(edge, defaultColor); // Initialize edge color to black
            } else {
                DefaultWeightedEdge edge = graph.addEdge(source, target);
                edgeWeights.put(edgeKey, 1);
                edgeColors.put(edge, defaultColor); // Initialize edge color to black
            }
        }
    }

    public void showDirectedGraph(String graphFilePath) {
        JGraphXAdapter<String, DefaultWeightedEdge> graphAdapter = new JGraphXAdapter<>(graph);
        mxIGraphLayout layout = new mxOrganicLayout(graphAdapter);
        layout.execute(graphAdapter.getDefaultParent());

        // Set edge colors
        for (DefaultWeightedEdge edge : graph.edgeSet()) {
            String color = edgeColors.get(edge);
            String style = mxConstants.STYLE_STROKECOLOR + "=" + color + ";" +
                    mxConstants.STYLE_FONTCOLOR + "=" + color + ";";
            graphAdapter.getEdgeToCellMap().get(edge).setStyle(style);

            double weight = graph.getEdgeWeight(edge);
            String label = String.format("%.2f", weight);
            graphAdapter.getEdgeToCellMap().get(edge).setValue(label);
        }

        // Create image
        BufferedImage image = mxCellRenderer.createBufferedImage(graphAdapter, null, 2, Color.WHITE, true, null);

        // Save image
        File imgFile = new File(graphFilePath);
        try {
            ImageIO.write(image, "PNG", imgFile);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // 打印WordGraph的全部信息
    public void printGraphInfo() {
        System.out.println("Vertices:");
        for (String vertex : graph.vertexSet()) {
            System.out.println(" - " + vertex);
        }

        System.out.println("\nEdges:");
        for (DefaultWeightedEdge edge : graph.edgeSet()) {
            String source = graph.getEdgeSource(edge);
            String target = graph.getEdgeTarget(edge);
            double weight = graph.getEdgeWeight(edge);
            System.out.println(" - " + source + " -> " + target + " (Weight: " + weight + ")");
        }
    }
    public List<String> queryBridgeWords(String word1, String word2) {
        List<String> bridgeWords = new ArrayList<>();

        // Check if word1 and word2 are in the graph
        if (!graph.containsVertex(word1) || !graph.containsVertex(word2)) {
            System.out.println("No " + (graph.containsVertex(word1) ? "word2" : "word1") + " in the graph!");
            return bridgeWords;
        }

        // Check if there is any bridge word between word1 and word2
        boolean bridgeFound = false;
        for (DefaultWeightedEdge edge : graph.edgeSet()) {
            String source = graph.getEdgeSource(edge);
            String target = graph.getEdgeTarget(edge);
            if (source.equalsIgnoreCase(word1) && graph.containsEdge(target, word2)) {
                bridgeWords.add(target);
                bridgeFound = true;
            }
        }

        // Output the result
        if (!bridgeFound) {
            System.out.println("No bridge words from " + word1 + " to " + word2 + "!");
        } else {
            System.out.print("The bridge words from " + word1 + " to " + word2 + " are: ");
            for (int i = 0; i < bridgeWords.size(); i++) {
                if (i > 0) {
                    System.out.print(", ");
                }
                System.out.print(bridgeWords.get(i));
            }
            System.out.println(".");
        }

        return bridgeWords;
    }
    public void calcShortestPath(String word) {
        if (!graph.containsVertex(word)) {
            System.out.println("No " + word + " in the graph!");
            return;
        }
        String timestamp = String.valueOf(System.currentTimeMillis());

        for (String otherWord : graph.vertexSet()) {
            if (!otherWord.equals(word)) {
                calcShortestPath(word, otherWord, "one_to_other/"+timestamp);
            }
        }
    }
    public void calcShortestPath(String word1, String word2, String folderName) {
        // 检查图中是否包含给定的单词
        if (!graph.containsVertex(word1) || !graph.containsVertex(word2)) {
            System.out.println("No " + word1 + " or " + word2 + " in the graph!");
            return;
        }

        // 使用Dijkstra算法计算最短路径
        DijkstraShortestPath<String, DefaultWeightedEdge> shortestPathAlg = new DijkstraShortestPath<>(graph);
        GraphPath<String, DefaultWeightedEdge> shortestPath = shortestPathAlg.getPath(word1, word2);

        // 如果找不到路径，则打印提示信息并返回
        if (shortestPath == null) {
            System.out.println("No path found between " + word1 + " and " + word2);
            return;
        }

        // 获取最短路径及其长度
        List<DefaultWeightedEdge> shortestPathEdges = shortestPath.getEdgeList();
        double shortestPathLength = shortestPath.getWeight();

        // 查找所有最短路径
        List<List<DefaultWeightedEdge>> allShortestPaths = new ArrayList<>();
        List<DefaultWeightedEdge> currentPath = new ArrayList<>();
        findAllShortestPaths(word1, word2, currentPath, allShortestPaths, shortestPathLength);

        // 根据所属最短路径的数量为边着色
        for (DefaultWeightedEdge edge : graph.edgeSet()) {
            String color = "#000000"; // 默认为黑色
            int pathCount = countPathsContainingEdge(allShortestPaths, edge);
            if (pathCount == 1) {
                color = "#FF0000"; // 只属于一个最短路径的边为红色
            } else if (pathCount > 1) {
                color = "#800080"; // 属于多个最短路径的边为紫色
            }
            edgeColors.put(edge, color);
        }

        // 渲染图形
        String graphFilePath = generateFilePath(word1, word2, folderName);
        showDirectedGraph(graphFilePath);

        // 打印所有最短路径及其长度
        System.out.println("All shortest paths between " + word1 + " and " + word2 + ":");
        for (List<DefaultWeightedEdge> path : allShortestPaths) {
            double pathLength = calculatePathLength(path);
            System.out.println("Path:");
            for (DefaultWeightedEdge edge : path) {
                String source = graph.getEdgeSource(edge);
                String target = graph.getEdgeTarget(edge);
                System.out.println(" - " + source + " -> " + target);
            }
            System.out.println("Path length: " + pathLength);
        }

        // 将边的颜色重置为黑色
        for (DefaultWeightedEdge edge : graph.edgeSet()) {
            edgeColors.put(edge, "#000000"); // 将边的颜色重置为黑色
        }
    }

    private void findAllShortestPaths(String currentWord, String targetWord, List<DefaultWeightedEdge> currentPath,
                                      List<List<DefaultWeightedEdge>> allShortestPaths, double shortestPathLength) {
        // 如果当前单词与目标单词相同，表示找到了一条路径
        if (currentWord.equals(targetWord)) {
            // 计算当前路径的长度
            double pathLength = calculatePathLength(currentPath);
            // 如果当前路径长度等于最短路径长度，则将当前路径加入到所有最短路径列表中
            if (pathLength == shortestPathLength) {
                allShortestPaths.add(new ArrayList<>(currentPath));
            }
            return;
        }

        // 遍历当前单词的所有出边
        for (DefaultWeightedEdge edge : graph.outgoingEdgesOf(currentWord)) {
            // 获取出边的目标单词
            String nextWord = graph.getEdgeTarget(edge);
            // 如果当前路径不包含该边，避免形成环路
            if (!currentPath.contains(edge)) {
                // 将该边加入当前路径
                currentPath.add(edge);
                // 递归查找以目标单词为终点的最短路径
                findAllShortestPaths(nextWord, targetWord, currentPath, allShortestPaths, shortestPathLength);
                // 回溯，将该边移出当前路径，以便尝试其他路径
                currentPath.remove(edge);
            }
        }
    }

    private double calculatePathLength(List<DefaultWeightedEdge> path) {
        double length = 0;
        for (DefaultWeightedEdge edge : path) {
            length += graph.getEdgeWeight(edge);
        }
        return length;
    }

    private int countPathsContainingEdge(List<List<DefaultWeightedEdge>> allPaths, DefaultWeightedEdge edge) {
        int count = 0;
        for (List<DefaultWeightedEdge> path : allPaths) {
            if (path.contains(edge)) {
                count++;
            }
        }
        return count;
    }
    private String generateFilePath(String word1, String word2, String fileType) {
        String timestamp = String.valueOf(System.currentTimeMillis());
        // 指定要创建文件夹的路径
        String directoryPath = "src/main/java/org/example/img/"+fileType+"/"+timestamp;
        osUtils.creatDirPath(directoryPath);
        String fileName = directoryPath+'/'+word1 + "-" + word2 + "-" + "ShortestPaths" + ".png";
        return  fileName;
    }
    public void randomWalk() {
        if (graph.vertexSet().isEmpty()) {
            System.out.println("Graph is empty!");
            return;
        }
        String timestamp = String.valueOf(System.currentTimeMillis());
        Random random = new Random();
        List<String> visitedNodes = new ArrayList<>();
        List<DefaultWeightedEdge> visitedEdges = new ArrayList<>();

        String currentNode = getRandomVertex(random);

        System.out.println("Starting random walk from node: " + currentNode);
        String logFileDir = "src/main/java/org/example/log/random_walk/"+timestamp;
        osUtils.creatDirPath(logFileDir);
        String logFilePath = logFileDir+"/walkTrack.txt";
        try (FileWriter writer = new FileWriter(logFilePath)) {
            writer.write("Random walk path:\n");
            while (true) {
                visitedNodes.add(currentNode);
                String line = "Visited node: " + currentNode + "\n";
                writer.write(line);

                List<DefaultWeightedEdge> outgoingEdges = new ArrayList<>(graph.outgoingEdgesOf(currentNode));
                if (outgoingEdges.isEmpty()) {
                    System.out.println("No outgoing edges from node " + currentNode + ". Random walk ended.");
                    break;
                }

                DefaultWeightedEdge nextEdge = getRandomEdge(random, outgoingEdges);
                while (visitedEdges.contains(nextEdge)) {
                    System.out.println("Edge " + nextEdge + " has already been visited. Exiting random walk.");
                    return;
                }
                visitedEdges.add(nextEdge);

                String nextNode = graph.getEdgeTarget(nextEdge);
                System.out.println("Next node: " + nextNode);
                String edgeColor = "#00FF00"; // green color for visited edges
                edgeColors.put(nextEdge, edgeColor);
                String randomDicPath = "src/main/java/org/example/img/random_walk/"+timestamp;
                osUtils.creatDirPath(randomDicPath);
                showDirectedGraph(randomDicPath+"/tempGraph.png");

                currentNode = nextNode;

                System.out.print("Continue random walk? (yes/no): ");
                Scanner scanner = new Scanner(System.in);
                String input = scanner.nextLine().toLowerCase();
                if (!input.equals("yes")) {
                    System.out.println("Random walk stopped by user.");
                    break;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        // Revert edge colors to black
        for (DefaultWeightedEdge edge : graph.edgeSet()) {
            edgeColors.put(edge, "#000000"); // Reset edge colors to black
        }
    }

    private String getRandomVertex(Random random) {
        List<String> vertices = new ArrayList<>(graph.vertexSet());
        return vertices.get(random.nextInt(vertices.size()));
    }

    private DefaultWeightedEdge getRandomEdge(Random random, List<DefaultWeightedEdge> edges) {
        return edges.get(random.nextInt(edges.size()));
    }
    public String generateNewText(String inputText) {
        StringBuilder newTextBuilder = new StringBuilder();
        String[] words = inputText.split("\\s+");
        Random random = new Random();

        newTextBuilder.append(words[0]).append(" "); // Append the first word

        for (int i = 0; i < words.length - 1; i++) {
            String word1 = words[i].toLowerCase();
            String word2 = words[i + 1].toLowerCase();

            // Check if word1 and word2 are in the graph
            if (!graph.containsVertex(word1) || !graph.containsVertex(word2)) {
                newTextBuilder.append(words[i + 1]).append(" ");
                continue;
            }

            // Get the bridge words between word1 and word2
            List<String> bridgeWords = queryBridgeWords(word1, word2);

            if (!bridgeWords.isEmpty()) {
                String randomBridgeWord = bridgeWords.get(random.nextInt(bridgeWords.size()));
                newTextBuilder.append(randomBridgeWord).append(" ");
            }

            // Append the next word if it's different from the bridge word
            if (!bridgeWords.contains(word2)) {
                newTextBuilder.append(words[i + 1]).append(" ");
            }
        }

        String newText = newTextBuilder.toString().trim();
        System.out.println("Generated new text: " + newText);
        return newText;
    }

    public String generateNewText() {
        Scanner scanner = new Scanner(System.in);
        String inputText;
        do {
            System.out.print("Enter the input text: ");
            inputText = scanner.nextLine();
            String filledText = generateNewText(inputText);
            System.out.print("Do you want to continue (yes/no)? ");
        } while (scanner.nextLine().equalsIgnoreCase("yes"));
        return inputText;
    }

}